Skip to content

feat(sqlserver): surface informational messages (STATISTICS, PRINT) in query results#307

Merged
tianzhou merged 2 commits into
bytebase:mainfrom
esetnik:feat/sqlserver-statistics-messages
Jun 7, 2026
Merged

feat(sqlserver): surface informational messages (STATISTICS, PRINT) in query results#307
tianzhou merged 2 commits into
bytebase:mainfrom
esetnik:feat/sqlserver-statistics-messages

Conversation

@esetnik

@esetnik esetnik commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Summary

SQL Server emits informational messages via the TDS protocol for SET STATISTICS TIME/IO output, PRINT statements, and warnings. The mssql driver's Request object exposes these through info events, but they were previously discarded.

This change captures those messages during query execution and includes them in the response when present, enabling MCP clients to access query performance statistics and diagnostic output.

Motivation

When using DBHub as an MCP server to analyze SQL Server query performance, there's currently no way to see SET STATISTICS TIME ON or SET STATISTICS IO ON output — the tool only returns result set rows. This forces users to work around the limitation with DECLARE @start timing variables or DMV queries, which provide less detail than the native statistics output.

With this change, running:

SET STATISTICS TIME ON;
SELECT TOP 10 * FROM users;
SET STATISTICS TIME OFF;

...will include the CPU/elapsed time breakdown in the response's messages array alongside the result rows.

Changes

  • src/connectors/interface.ts: Add optional messages?: string[] field to SQLResult
  • src/connectors/sqlserver/index.ts: Listen for info events on the Request object before executing queries, collect messages, and include them in the result when present
  • src/tools/execute-sql.ts: Pass messages through to the MCP tool response when non-empty

Notes

  • The change is backward-compatible — messages is optional and only included when non-empty
  • Only the SQL Server connector is affected; other connectors continue to return the same SQLResult shape
  • The mssql package's Request.on('info') event covers all SQL Server informational messages (severity < 10): statistics, print output, warnings, etc.

…n query results

SQL Server emits informational messages via the TDS protocol for
SET STATISTICS TIME/IO output, PRINT statements, and warnings.
The mssql driver's Request object exposes these through 'info' events,
but they were previously discarded.

This change captures those messages during query execution and includes
them in the response when present, enabling MCP clients to access
query performance statistics and diagnostic output.

Changes:
- Add optional `messages` field to SQLResult interface
- Listen for 'info' events on SQL Server Request before execution
- Include non-empty messages array in execute_sql tool response
Copilot AI review requested due to automatic review settings April 8, 2026 18:36
@esetnik esetnik requested a review from tianzhou as a code owner April 8, 2026 18:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for surfacing SQL Server informational messages (e.g., SET STATISTICS TIME/IO, PRINT, warnings) by capturing mssql Request info events during execution and returning them to MCP clients when present.

Changes:

  • Extend SQLResult with an optional messages?: string[] field for informational DB messages.
  • Capture SQL Server Request info events during query execution and include them in the connector result.
  • Pass messages through the execute_sql tool response when non-empty.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/connectors/interface.ts Adds optional messages to the shared SQLResult contract.
src/connectors/sqlserver/index.ts Collects SQL Server informational messages via Request.on('info') and returns them in SQLResult.
src/tools/execute-sql.ts Includes messages in the tool response payload when provided by the connector.

Comment thread src/tools/execute-sql.ts
Comment thread src/connectors/sqlserver/index.ts
…ture

Adds three tests covering the new messages behavior:
- PRINT output is captured and returned in messages array
- SET STATISTICS TIME output contains CPU/elapsed timing info
- messages field is absent when no informational messages are emitted

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment on lines 630 to 632
return {
rows: result.recordset || [],
rowCount: result.rowsAffected[0] || 0,

Copilot AI Apr 9, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rowCount is derived from result.rowsAffected[0], which is incorrect for multi-statement batches (e.g. SET STATISTICS TIME ON; SELECT ...; SET STATISTICS TIME OFF; or PRINT ...; SELECT ...;). In those cases the first statement often affects 0 rows, so count will be reported as 0 even when recordset contains rows. Consider computing rowCount based on the returned recordset (for SELECT) and/or using the last entry in rowsAffected (or summing it) to reflect the statement(s) that actually produced the returned rows.

Suggested change
return {
rows: result.recordset || [],
rowCount: result.rowsAffected[0] || 0,
const rows = result.recordset || [];
const rowCount = rows.length > 0
? rows.length
: (result.rowsAffected && result.rowsAffected.length > 0
? result.rowsAffected[result.rowsAffected.length - 1] || 0
: 0);
return {
rows,
rowCount,

Copilot uses AI. Check for mistakes.

@tianzhou tianzhou left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tianzhou tianzhou merged commit e202b05 into bytebase:main Jun 7, 2026
6 checks passed
tianzhou added a commit that referenced this pull request Jun 7, 2026
Promote the informational-messages field added in #307 from a bare
string[] to a structured DatabaseMessage[] so severity, code, and line
are preserved instead of discarded. This is a cross-database shape:
PostgreSQL NOTICE/WARNING and MySQL warnings can populate the same type
later without a breaking interface change.

- interface.ts: add DatabaseMessage type; SQLResult.messages is now
  DatabaseMessage[]
- sqlserver: map the mssql `info` event (message/class/number/lineNumber)
  into DatabaseMessage; severity lets clients distinguish PRINT from warnings
- execute_sql forwards the structured array unchanged
- update SQL Server integration tests to assert on `.text`

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants